/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate alloc;

use crate::commands::common_command::*;
use crate::common::types::*;
use crate::common::{AuthType, ErrorCode, UserType};
use crate::entry::user_auth_ffi::CommandId;
use crate::entry::user_auth_ffi::*;
use crate::traits::command_manager::{AccessPolicy, CommandHandler};
use crate::traits::edit_session_manager::EditSessionManagerRegistry;
use crate::ROOT_SECRET_LEN;
use crate::{impl_command_handler, log_e, log_i, Box};
use crate::Vec;
use core::convert::TryFrom;

// BeginEnrollment - implementation
#[derive(Clone)]
pub struct EnrollParam {
    pub auth_type: AuthType,
    pub executor_sensor_hint: u32,
    pub user_id: i32,
    pub user_type: UserType,
    pub auth_sub_type: i32,
}

impl TryFrom<EnrollParamFfi> for EnrollParam {
    type Error = ErrorCode;

    fn try_from(ffi: EnrollParamFfi) -> Result<Self, ErrorCode> {
        Ok(EnrollParam {
            auth_type: AuthType::try_from(ffi.auth_type)?,
            executor_sensor_hint: ffi.executor_sensor_hint,
            user_id: ffi.user_id,
            user_type: UserType::try_from(ffi.user_type)?,
            auth_sub_type: ffi.auth_sub_type,
        })
    }
}

#[derive(Clone)]
pub struct BeginEnrollmentInput {
    pub auth_token: Vec<u8>,
    pub param: EnrollParam,
}

impl TryFrom<BeginEnrollmentInputFfi> for BeginEnrollmentInput {
    type Error = ErrorCode;

    fn try_from(ffi: BeginEnrollmentInputFfi) -> Result<Self, ErrorCode> {
        Ok(BeginEnrollmentInput {
            auth_token: Vec::try_from(ffi.auth_token)?,
            param: ffi.param.try_into()?,
        })
    }
}

pub type BeginEnrollmentOutput = BeginEnrollmentOutputFfi;

pub trait BeginEnrollmentCommandExecutor {
    fn execute(&self, input: BeginEnrollmentInput) -> Result<BeginEnrollmentOutput, ErrorCode>;
}

pub struct DefaultBeginEnrollmentCommandExecutor;

impl BeginEnrollmentCommandExecutor for DefaultBeginEnrollmentCommandExecutor {
    fn execute(&self, _input: BeginEnrollmentInput) -> Result<BeginEnrollmentOutput, ErrorCode> {
        Ok(BeginEnrollmentOutput {
            info: ScheduleInfoFfi::default(),
        })
    }
}

impl_command_handler!(
    BeginEnrollmentCommandHandler,
    BeginEnrollmentInputFfi,
    BeginEnrollmentInput,
    BeginEnrollmentOutputFfi,
    Box<dyn BeginEnrollmentCommandExecutor>
);

// CancelEnrollment - implementation
pub type CancelEnrollmentInput = CancelEnrollmentInputFfi;
pub type CancelEnrollmentOutput = CancelEnrollmentOutputFfi;

pub trait CancelEnrollmentCommandExecutor {
    fn execute(&self, input: CancelEnrollmentInput) -> Result<CancelEnrollmentOutput, ErrorCode>;
}

pub struct DefaultCancelEnrollmentCommandExecutor;

impl CancelEnrollmentCommandExecutor for DefaultCancelEnrollmentCommandExecutor {
    fn execute(&self, _input: CancelEnrollmentInput) -> Result<CancelEnrollmentOutput, ErrorCode> {
        Err(ErrorCode::GeneralError)
    }
}

impl_command_handler!(
    CancelEnrollmentCommandHandler,
    CancelEnrollmentInputFfi,
    CancelEnrollmentInput,
    CancelEnrollmentOutputFfi,
    Box<dyn CancelEnrollmentCommandExecutor>
);

// UpdateEnrollmentResult - implementation
#[derive(Debug, Clone)]
pub struct UpdateEnrollmentResultInput {
    pub user_id: i32,
    pub schedule_result: Vec<u8>,
}

impl TryFrom<UpdateEnrollmentResultInputFfi> for UpdateEnrollmentResultInput {
    type Error = ErrorCode;

    fn try_from(value: UpdateEnrollmentResultInputFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(UpdateEnrollmentResultInput {
            user_id: value.user_id,
            schedule_result: value.schedule_result.try_into()?,
        })
    }
}

#[derive(Clone, Default)]
pub struct EnrollResultInfo {
    pub credential_id: CredentialId,
    pub old_info: CredentialInfo,
    pub root_secret: Option<RootSecret>,
    pub old_root_secret: Option<RootSecret>,
    pub auth_token: Vec<u8>,
}

impl TryFrom<EnrollResultInfo> for EnrollResultInfoFfi {
    type Error = ErrorCode;

    fn try_from(value: EnrollResultInfo) -> core::result::Result<Self, ErrorCode> {
        Ok(EnrollResultInfoFfi {
            credential_id: value.credential_id.0,
            old_info: value.old_info.try_into()?,
            root_secret: value.root_secret.try_into()?,
            old_root_secret: value.old_root_secret.try_into()?,
            auth_token: value.auth_token.try_into()?, 
        })
    }
}

#[derive(Clone, Default)]
pub struct UpdateEnrollmentResultOutput {
    pub info: EnrollResultInfo,
}

impl TryFrom<UpdateEnrollmentResultOutput> for UpdateEnrollmentResultOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: UpdateEnrollmentResultOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(UpdateEnrollmentResultOutputFfi {
            info: value.info.try_into()?,
        })
    }
}

pub trait UpdateEnrollmentResultCommandExecutor {
    fn execute(
        &self,
        input: UpdateEnrollmentResultInput,
    ) -> Result<UpdateEnrollmentResultOutput, ErrorCode>;
}

pub struct DefaultUpdateEnrollmentResultCommandExecutor;

impl UpdateEnrollmentResultCommandExecutor for DefaultUpdateEnrollmentResultCommandExecutor {
    fn execute(
        &self,
        _input: UpdateEnrollmentResultInput,
    ) -> Result<UpdateEnrollmentResultOutput, ErrorCode> {
        Err(ErrorCode::GeneralError)
    }
}

impl_command_handler!(
    UpdateEnrollmentResultCommandHandler,
    UpdateEnrollmentResultInputFfi,
    UpdateEnrollmentResultInput,
    UpdateEnrollmentResultOutputFfi,
    Box<dyn UpdateEnrollmentResultCommandExecutor>
);

// OpenSession - implementation
pub type OpenSessionInput = OpenSessionInputFfi;
pub type OpenSessionOutput = OpenSessionOutputFfi;

pub trait OpenSessionCommandExecutor {
    fn execute(&self, input: OpenSessionInput) -> Result<OpenSessionOutput, ErrorCode>;
}

pub struct DefaultOpenSessionCommandExecutor;

impl OpenSessionCommandExecutor for DefaultOpenSessionCommandExecutor {
    fn execute(&self, _input: OpenSessionInput) -> Result<OpenSessionOutput, ErrorCode> {
        let edit_session = EditSessionManagerRegistry::get_mut().open_edit_session(_input.user_id)?;
        Ok(OpenSessionOutput { challenge: *edit_session.challenge() })
    }
}

impl_command_handler!(
    OpenSessionCommandHandler,
    OpenSessionInputFfi,
    OpenSessionInput,
    OpenSessionOutputFfi,
    Box<dyn OpenSessionCommandExecutor>
);

// CloseSession - implementation
pub type CloseSessionInput = CloseSessionInputFfi;
pub type CloseSessionOutput = CloseSessionOutputFfi;

pub trait CloseSessionCommandExecutor {
    fn execute(&self, input: CloseSessionInput) -> Result<CloseSessionOutput, ErrorCode>;
}

pub struct DefaultCloseSessionCommandExecutor;

impl CloseSessionCommandExecutor for DefaultCloseSessionCommandExecutor {
    fn execute(&self, _input: CloseSessionInput) -> Result<CloseSessionOutput, ErrorCode> {
        EditSessionManagerRegistry::get_mut().close_edit_session();
        Ok(CloseSessionOutput::default())
    }
}

impl_command_handler!(
    CloseSessionCommandHandler,
    CloseSessionInputFfi,
    CloseSessionInput,
    CloseSessionOutputFfi,
    Box<dyn CloseSessionCommandExecutor>
);

// UpdateAbandonResult - implementation
pub type UpdateAbandonResultInput = UpdateAbandonResultInputFfi;
pub type UpdateAbandonResultOutput = UpdateAbandonResultOutputFfi;

pub trait UpdateAbandonResultCommandExecutor {
    fn execute(
        &self,
        input: UpdateAbandonResultInput,
    ) -> Result<UpdateAbandonResultOutput, ErrorCode>;
}

pub struct DefaultUpdateAbandonResultCommandExecutor;

impl UpdateAbandonResultCommandExecutor for DefaultUpdateAbandonResultCommandExecutor {
    fn execute(
        &self,
        _input: UpdateAbandonResultInput,
    ) -> Result<UpdateAbandonResultOutput, ErrorCode> {
        Ok(UpdateAbandonResultOutput {
            infos: CredentialArrayFfi::default(),
        })
    }
}

impl_command_handler!(
    UpdateAbandonResultCommandHandler,
    UpdateAbandonResultInputFfi,
    UpdateAbandonResultInput,
    UpdateAbandonResultOutputFfi,
    Box<dyn UpdateAbandonResultCommandExecutor>
);
